package simpledb;

import simpledb.common.Database;
import simpledb.common.Type;
import simpledb.execution.*;
import simpledb.storage.HeapFile;
import simpledb.storage.IntField;
import simpledb.storage.Tuple;
import simpledb.storage.TupleDesc;
import simpledb.transaction.TransactionId;

import java.io.File;

/**
 * 本类用于测试两个表之间的 Join 操作。
 * 使用 SeqScan 扫描两个表的数据，并结合 Filter 和 JoinPredicate 实现条件过滤和连接操作。
 * 以下代码在两个表之间实现一个简单的联接查询，每个表由三列整数组成。
 * 文件 some_data_file1.dat 和 some_data_file2.dat 是此文件中页面的二进制表示形式
 * 此代码等效于 SQL 语句：
 * SELECT *
 * FROM some_data_file1,
 *      some_data_file2
 * WHERE some_data_file1.field1 = some_data_file2.field1
 *      AND some_data_file1.id > 1
 */
public class lab2_JoinQueryTest {

    public static void main(String[] argv) {
        // 构建一个包含 3 个字段的表结构（schema）
        Type types[] = new Type[]{Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE};
        String names[] = new String[]{"field0", "field1", "field2"};

        // 创建 TupleDesc 对象，描述表的字段类型和字段名
        TupleDesc td = new TupleDesc(types, names);

        // 创建两个 HeapFile 表对象，分别关联磁盘上的数据文件 some_data_file1.dat 和 some_data_file2.dat
        // 并通过 Catalog 注册这两张表，表名分别为 "t1" 和 "t2"
        HeapFile table1 = new HeapFile(new File("some_data_file1.dat"), td);
        Database.getCatalog().addTable(table1, "t1");

        HeapFile table2 = new HeapFile(new File("some_data_file2.dat"), td);
        Database.getCatalog().addTable(table2, "t2");

        // 构造查询语句：
        // 使用两个 SeqScan 扫描器读取两张表的数据，
        // 然后将其中一个扫描器的结果传给 Filter 做条件过滤，
        // 最后再与另一个扫描器的结果进行 Join 连接。

        TransactionId tid = new TransactionId(); // 创建事务 ID

        // 创建两个顺序扫描器，分别扫描 t1 和 t2 表的数据
        SeqScan ss1 = new SeqScan(tid, table1.getId(), "t1");
        SeqScan ss2 = new SeqScan(tid, table2.getId(), "t2");

        // 创建一个 Filter，对第一个扫描器（ss1）的结果进行过滤
        // 条件是：第 0 号字段（field0）的值 > 1
        Filter sf1 = new Filter(
                new Predicate(0,
                        Predicate.Op.GREATER_THAN, new IntField(1)), ss1);

        // 创建 Join 的谓词条件：使用 t1.field1 等于 t2.field1 作为连接条件
        // 即：t1.field1 == t2.field1
        JoinPredicate p = new JoinPredicate(1, Predicate.Op.EQUALS, 1);

        // 创建 Join 操作，将过滤后的结果与第二个扫描器的结果进行连接
        Join j = new Join(p, sf1, ss2);

        // 开始执行查询并输出结果
        try {
            j.open(); // 打开 Join 操作
            while (j.hasNext()) { // 遍历所有连接结果
                Tuple tup = j.next(); // 获取下一条元组
                System.out.println(tup); // 输出该元组
            }
            j.close(); // 关闭 Join 操作

            // 提交事务，释放资源
            Database.getBufferPool().transactionComplete(tid);

        } catch (Exception e) {
            e.printStackTrace(); // 捕获并打印异常信息
        }

    }

}
